#include "simple_timer.h"

namespace qb
{
	simple_timer::simple_timer() 
		: m_expired(true), m_try_to_expire(false) 
	{
	}

	simple_timer::simple_timer(const simple_timer& t)
	{
		m_expired = t.m_expired.load();
		m_try_to_expire = t.m_try_to_expire.load();
	}

	simple_timer::~simple_timer()
	{
		stop();
	}

	void simple_timer::start(int interval, std::function<void()> task)
	{
		if ( m_expired == false )
			return;

		m_expired = false;
		m_try_to_expire = false;
		std::thread([this, interval, task]() mutable
		{
			const int basic_unit = 2000;
			const int remainder = interval % basic_unit;
			const int recycles = interval / basic_unit;
			if ( 0 != recycles )
				interval = basic_unit;

			int complement = remainder;
			int recycle_count = 1;
			while ( !m_try_to_expire ) 
			{
#ifdef WIN32
				::Sleep(interval);	// avoid sleep_until implementation bug.
#else
				std::this_thread::sleep_for(std::chrono::milliseconds(interval));
#endif
				if ( recycles == 0 )
					task();
				else 
				{
					if ( recycles == recycle_count )
					{
						if ( complement == 0 )
						{
							task();

							recycle_count = 1;
							interval = basic_unit;
							complement = remainder;
						}
						else
						{
							interval = complement;
							complement = 0;
						}
					}
					else
						recycle_count++;
				}
			}
			{
				std::lock_guard<std::mutex> locker(m_mutex);
				m_expired = true;
				m_expired_cond.notify_one();
			}
		}).detach();
	}

	/*
	* this is a asynchronization function, only notify simple_timer to stop, but
	* not wait simple_timer thread exit.
	*/
	void simple_timer::notify_stop()
	{
		m_try_to_expire = true;
	}

	/*
	* this is a synchronization function, notify simple_timer to stop and wait
	* simple_timer thread finishing callback. so this function can't be called
	* in simple_timer callback function.
	*/
	void simple_timer::stop()
	{
		if ( m_expired || m_try_to_expire )
			return;

		m_try_to_expire = true;
		{
			std::unique_lock<std::mutex> locker(m_mutex);
			m_expired_cond.wait(locker, [this] { return m_expired == true; });
			m_try_to_expire = false;
		}
	}
}